﻿using System;
using System.ComponentModel.Composition;
using System.Linq;
using System.Windows.Forms;
using DLaB.Xrm;
using DLaB.XrmToolboxCommon;
using Microsoft.Xrm.Sdk.Messages;
using Microsoft.Xrm.Sdk.Metadata;
using XrmToolBox.Extensibility;
using XrmToolBox.Extensibility.Interfaces;

namespace DLaB.LocalToGlobalOptionSet
{
    public partial class LocalToGlobalOptionSet : PluginControlBase
    {
        private bool AttributesNeedLoaded { get; set; }

        public LocalToGlobalOptionSet()
        {
            InitializeComponent();
        }

        public void LoadEntities()
        {
            WorkAsync("Retrieving Entities...", e =>
            {
                e.Result = Service.Execute(new RetrieveAllEntitiesRequest() { EntityFilters = EntityFilters.Entity, RetrieveAsIfPublished = true });
            }, e =>
            {
                cmbEntities.BeginUpdate();
                cmbEntities.Items.Clear();

                var result = ((RetrieveAllEntitiesResponse)e.Result).EntityMetadata.
                    Select(m => new ObjectCollectionItem<EntityMetadata>(m.DisplayName.GetLocalOrDefaultText("N/A") + " (" + m.LogicalName + ")",m)).
                    OrderBy(r => r.DisplayName);

                cmbEntities.Items.AddRange(result.ToArray());
                cmbEntities.EndUpdate();
            });
        }

        public void LoadAttributes()
        {
            var entity = cmbEntities.SelectedItem as ObjectCollectionItem<EntityMetadata>;
            if(entity == null){
                return;
            }

            WorkAsync("Retrieving Attributes...", e =>
            {
                e.Result = Service.Execute(new RetrieveEntityRequest() { 
                    LogicalName = entity.Value.LogicalName,
                    EntityFilters = EntityFilters.Attributes, 
                    RetrieveAsIfPublished = true });
            }, e =>
            {
                cmbAttributes.BeginUpdate();
                cmbAttributes.Items.Clear();

                var result = ((RetrieveEntityResponse)e.Result).EntityMetadata.Attributes.Where(a => a.IsLocalOptionSetAttribute() && a.IsManaged == false).
                    Select(a => new ObjectCollectionItem<AttributeMetadata>((a.DisplayName.GetLocalOrDefaultText("N/A")) + " (" + a.LogicalName + ")", a)).
                    OrderBy(r => r.DisplayName);

                cmbAttributes.Items.AddRange(result.ToArray());

                cmbAttributes.EndUpdate();
            });
        }

        private void CheckAllSteps()
        {
            clbSteps.ItemCheck -= new ItemCheckEventHandler(clbSteps_ItemCheck);
            for (int i = 0; i < clbSteps.Items.Count; i++)
            {
                clbSteps.SetItemChecked(i, true);
            }
            clbSteps.ItemCheck += new ItemCheckEventHandler(clbSteps_ItemCheck);
        }

        public void ExecuteSteps()
        {
            this.Enabled = false;
            var attribute = cmbAttributes.SelectedItem as ObjectCollectionItem<AttributeMetadata>;
            if(attribute == null)
            {
                MessageBox.Show("No Attribute Selected!", "Unable To Execute", MessageBoxButtons.OK, MessageBoxIcon.Information);
                this.Enabled = true;
                return;
            }
            Logic.Steps steps = 0;
            foreach(int step in clbSteps.CheckedIndices){
                steps |= (Logic.Steps) Math.Pow(2, step);
            }

            if(steps == 0){
                MessageBox.Show("No Steps Selected!", "Unable To Execute", MessageBoxButtons.OK, MessageBoxIcon.Information);
                this.Enabled = true;
                return;
            }

            if (String.IsNullOrWhiteSpace(txtGlobalOptionSetName.Text)) {
                MessageBox.Show("Invalid Global Option Set Name", "Unable To Execute", MessageBoxButtons.OK, MessageBoxIcon.Information);
                this.Enabled = true;
                return;
            }

            Logic.LogHandler onLog;

            WorkAsync("Performing Steps...", (w,e) => 
            {
                var optionSetName = attribute.Value.LogicalName;
                if (optionSetName.EndsWith(txtAttributePostfix.Text))
                {
                    optionSetName = optionSetName.Remove(optionSetName.LastIndexOf(txtAttributePostfix.Text));
                }

                var migrator = new Logic(Service, ConnectionDetail, txtAttributePostfix.Text, chkMigrate.Checked, chkConvertValues.Checked && chkConvertValues.Visible);
                onLog = (m) => w.ReportProgress(0, m);
                migrator.OnLog += onLog;
                try
                {
                    try
                    {
                        migrator.Run(attribute.Value.EntityLogicalName, optionSetName, txtGlobalOptionSetName.Text, txtGlobalPrefix.Text, steps);
                        w.ReportProgress(99, "Steps Completed!");
                    }
                    catch (InvalidOperationException ex)
                    {
                        w.ReportProgress(int.MinValue, ex.Message);
                    }
                    catch (Exception ex)
                    {
                        w.ReportProgress(int.MinValue, ex.ToString());
                    }
                }
                finally
                {
                    migrator.OnLog -= onLog;
                }

            }, e =>
            {
                this.Enabled = true;
            }, e => {             
                var text = e.UserState.ToString();
                if (e.ProgressPercentage != int.MinValue)
                {
                    SetWorkingMessage(text);
                }
                txtLog.AppendText(text + Environment.NewLine);
            });
        }

        #region Event Handlers

        private void btnLoadEntities_Click(object sender, EventArgs e)
        {
            ExecuteMethod(LoadEntities);
        }

        private void tsbClose_Click(object sender, EventArgs e)
        {
            CloseTool();
        }

        #region cmbEntities

        private void cmbEntities_SelectedIndexChanged(object sender, EventArgs e)
        {
            AttributesNeedLoaded = true;
        }

        private void cmbEntities_Leave(object sender, EventArgs e)
        {
            if (AttributesNeedLoaded)
            {
                ExecuteMethod(LoadAttributes);
                AttributesNeedLoaded = false;
            }
        }

        #endregion // cmbEntities


        private void LocalToGlobalOptionSet_Load(object sender, EventArgs e)
        {
            CheckAllSteps();
            lblOsvPrefix.Visible = false;
            txtGlobalPrefix.Visible = false;
            AttributesNeedLoaded = false;
        }

        #region cmbAttributes

        private void cmbAttributes_SelectedIndexChanged(object sender, EventArgs e)
        {
            btnExecuteSteps.Enabled = cmbAttributes.SelectedText == String.Empty;
            txtGlobalOptionSetName.Text = (cmbAttributes.SelectedItem as ObjectCollectionItem<AttributeMetadata>).Value.LogicalName;
        }

        private void cmbAttributes_MouseEnter(object sender, EventArgs e)
        {
            if (AttributesNeedLoaded)
            {
                ExecuteMethod(LoadAttributes);
                AttributesNeedLoaded = false;
            }
        }

        #endregion // cmbAttributes

        private void clbSteps_ItemCheck(object sender, ItemCheckEventArgs e)
        {
            clbSteps.ItemCheck -= new ItemCheckEventHandler(clbSteps_ItemCheck);
            if (e.NewValue == CheckState.Unchecked)
            {
                if (e.Index != 0 && clbSteps.GetItemChecked(e.Index - 1))
                {
                    for (int i = e.Index + 1; i < clbSteps.Items.Count; i++)
                    {
                        clbSteps.SetItemChecked(i, false);
                    }
                }
            }
            else if (e.NewValue == CheckState.Checked)
            {
                bool hasPostUnchecked = false;
                for (int i = e.Index + 1; i < clbSteps.Items.Count; i++)
                {
                    if (!clbSteps.GetItemChecked(i))
                    {
                        hasPostUnchecked = true;
                    }
                    else if (hasPostUnchecked)
                    {
                        // Check previous
                        for (int p = e.Index + 1; p < i; p++)
                        {
                            clbSteps.SetItemChecked(p, true);
                        }
                        break;
                    }
                }

                bool hasPreUnchecked = false;
                for (int i = e.Index - 1; i >= 0; i--)
                {
                    if (!clbSteps.GetItemChecked(i))
                    {
                        hasPreUnchecked = true;
                    }
                    else if (hasPreUnchecked)
                    {
                        // Check previous
                        for (int p = i; p < e.Index; p++)
                        {
                            clbSteps.SetItemChecked(p, true);
                        }
                        break;
                    }
                }
            }
                    clbSteps.ItemCheck += new ItemCheckEventHandler(clbSteps_ItemCheck);
        }

        private void btnExecuteSteps_Click(object sender, EventArgs e)
        {
            ExecuteMethod(ExecuteSteps);
        }

        private void chkMigrate_CheckedChanged(object sender, EventArgs e)
        {
            chkConvertValues.Visible = chkMigrate.Checked;
            txtGlobalPrefix.Visible = chkConvertValues.Visible && chkConvertValues.Checked;
            lblOsvPrefix.Visible = txtGlobalPrefix.Visible;
        }

        private void chkConvertValues_CheckedChanged(object sender, EventArgs e)
        {
            txtGlobalPrefix.Visible = chkConvertValues.Checked;
            lblOsvPrefix.Visible = txtGlobalPrefix.Visible;
        }

        #endregion // Event Handlers

        private void Tip_Popup(object sender, PopupEventArgs e)
        {

        }

    }

    [Export(typeof(IXrmToolBoxPlugin)),
     ExportMetadata("Name", "Local To Global Option Set Converter"),
     ExportMetadata("Description", "Converts a Local Option Set to a Global Option Set."),
     ExportMetadata("SmallImageBase64", SmallImage32X32), // null for "no logo" image or base64 image content 
     ExportMetadata("BigImageBase64", LargeImage120X120), // null for "no logo" image or base64 image content 
     ExportMetadata("BackgroundColor", "White"), // Use a HTML color name
     ExportMetadata("PrimaryFontColor", "#000000"), // Or an hexadecimal code
     ExportMetadata("SecondaryFontColor", "DarkGray")]
    public class EarlyBoundGenerator : PluginFactory
    {
        public override IXrmToolBoxPluginControl GetControl()
        {
            return new LocalToGlobalOptionSet();
        }
    }
}
